package common.sql;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import models.AsyncFlowData;
import models.ExecuteSql;
import org.apache.commons.lang.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 构建执行SQL
 * @author: lgq07612
 * @create: 2020-08-08 14:40
 **/
@SuppressWarnings("unchecked")
public class AsyncFlowBuildSql {
    /**
     * @param asyncFlowData 上下文
     * @param sql           执行的sql
     * @param dbName        数据库名
     * @param isMustExecute 是否必须执行
     */
    public static void operatorDataBySql(AsyncFlowData asyncFlowData, String sql, String dbName, boolean isMustExecute) throws IllegalAccessException {
        //数据库不正确
        if (asyncFlowData.getDataBaseInfo().size() == 0 || !asyncFlowData.getDataBaseInfo().containsKey(dbName)) {
            throw new IllegalAccessException("当前节点位配置数据源，请检查asyncflow.yml文件是否有db-" + dbName + "开头的数据库名称和对应SessionFactory的配置");
        }
        ExecuteSql execute = new ExecuteSql();
        execute.setDbname(dbName);
        execute.setMustExecute(isMustExecute);
        if (!sql.trim().endsWith(";")) {
            sql = sql + ";";
        }
        String typeString = sql.trim().substring(0, 10);
        if (typeString.toLowerCase().contains("insert")) {
            execute.setType(1);
        } else if (typeString.toLowerCase().contains("update")) {
            execute.setType(2);
            if (!sql.toLowerCase().contains("where")) {
                throw new IllegalArgumentException("更新语句必须带where条件");
            }
        } else {
            throw new IllegalArgumentException("SQL语句不正确,不是插入或者更新");
        }
        execute.setExecuteSql(sql);
        execute.setExecuteSqlLog(sql);
        asyncFlowData.getExecuteSQLs().add(execute);
    }


    /**
     * @param asyncFlowData 上下文
     * @param t             实体
     * @param dbName        数据库
     * @param isMustExecute 是否必须执行
     * @param <T>           泛型
     */
    public static <T> void insertByEntity(AsyncFlowData asyncFlowData, T t, String dbName, boolean isMustExecute) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        //数据库不正确
        if (asyncFlowData.getDataBaseInfo().size() == 0 || !asyncFlowData.getDataBaseInfo().containsKey(dbName)) {
            throw new IllegalAccessException("当前节点位配置数据源，请检查asyncflow.yml文件是否有db-\" + dbName + \"开头的数据库名称和对应SessionFactory的配置");
        }
        Class<?> tClass = t.getClass();
        //表名称
        String tableName = "";
        Annotation[] tableMethods = tClass.getAnnotations();
        for (Annotation annotation : tableMethods) {
            //获取方法名
            if (annotation instanceof TableName) {
                tableName = ((TableName) annotation).value();
                break;
            }
        }
        if (StringUtils.isBlank(dbName) || StringUtils.isBlank(tableName)) {
            throw new IllegalAccessException("表名为空，请检查当前实体是否有@TableName注解");
        }


        String tableIdName = "";
        long tableId = -1L;


        Field[] field;

        //获取所有字段
        field = tClass.getDeclaredFields();


        for (Field value : field) {
            if (tableId >= 0) {
                break;
            }
            Annotation[] ids = value.getAnnotations();
            for (Annotation annotation : ids) {
                value.setAccessible(true);
                //获取方法名
                if (annotation instanceof TableId) {
                    //获取属性的名字
                    String name = value.getName();
                    //将属性名字的首字母大写
                    name = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
                    //整合出 get() 属性这个方法
                    Method m = tClass.getMethod("get" + name);
                    Object tableIdObj = m.invoke(t);
                    if (tableIdObj == null) {
                        tableId = 0L;

                    } else {
                        tableId = (long) tableIdObj;
                    }
                    tableIdName = value.getName();
                    break;
                }
            }
        }
        //主键名称是空或者未获取到主键值
        if (StringUtils.isBlank(tableIdName) || tableId != 0) {
            throw new IllegalAccessException("插入语句tableId必须是null或者0；【tableId=" + tableId + "】【tableIdName=" + tableIdName + "】");
        }
        Map<String, Object> insertChangeValues = new HashMap<>();
        for (Field value : field) {

            //获取属性的名字
            String fieldName = value.getName();
            //将属性名字的首字母大写
            fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName.substring(0, 1).toUpperCase());
            //整合出 get() 属性这个方法
            Method fieldM = tClass.getMethod("get" + fieldName);
            Object fieldObj = fieldM.invoke(t);
            if (fieldObj != null) {
                insertChangeValues.put(value.getName(), fieldObj);
            }
        }
        ExecuteSql execute = new ExecuteSql();
        execute.setTableName(tableName);
        execute.setDbname(dbName);
        execute.setMustExecute(isMustExecute);
        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();
        StringBuilder s3 = new StringBuilder();
        StringBuilder s4 = new StringBuilder();
        StringBuilder s5 = new StringBuilder();


        if (insertChangeValues.size() == 0) {
            return;
        }
        Map<String, Object> params = new HashMap<>();
        for (Map.Entry<String, Object> entry : insertChangeValues.entrySet()) {
            String mapKey = entry.getKey();
            Object mapValue = entry.getValue();
            if (mapKey.toLowerCase().equals(tableIdName.toLowerCase())) {
                continue;
            }
            s1.append(mapKey).append(",");
            s2.append("#{").append(mapKey).append("},");
            s3.append("'").append(mapValue).append("',");
            params.put(mapKey, mapValue);

        }
        s4.append("INSERT INTO ").append(tableName).append("(").append(s1.toString(), 0, s1.length() - 1).append(") VALUES (").append(s2.toString(), 0, s2.length() - 1).append(");");
        s5.append("INSERT INTO ").append(tableName).append("(").append(s1.toString(), 0, s1.length() - 1).append(") VALUES (").append(s3.toString(), 0, s3.length() - 1).append(");");
        execute.setExecuteSql(s4.toString());
        execute.setExecuteSqlLog(s5.toString());
        execute.setType(1);
        execute.setParamsList(params);
        asyncFlowData.getExecuteSQLs().add(execute);
    }


    /**
     * @param asyncFlowData 上下文
     * @param t             实体
     * @param dbName        数据库
     * @param isMustExecute 是否必须执行
     * @param <T>           泛型
     */
    public static <T> void updateByEntity(AsyncFlowData asyncFlowData, T t, String dbName, boolean isMustExecute) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {

        //数据库不正确
        if (asyncFlowData.getDataBaseInfo().size() == 0 || !asyncFlowData.getDataBaseInfo().containsKey(dbName)) {
            throw new IllegalAccessException("当前节点位配置数据源，请检查asyncflow.yml文件是否有db-开头的数据库名称和对应SessionFactory的配置");
        }
        Class<?> tClass = t.getClass();
        //表名称
        String tableName = "";
        Annotation[] tableMethods = tClass.getAnnotations();
        for (Annotation annotation : tableMethods) {
            //获取方法名
            if (annotation instanceof TableName) {
                tableName = ((TableName) annotation).value();
                break;
            }
        }
        if (StringUtils.isBlank(dbName) || StringUtils.isBlank(tableName)) {
            throw new IllegalAccessException("表名为空，请检查当前实体是否有@TableName注解");
        }

        String tableIdName = "";
        long tableId = -1L;


        Field[] field;

        //获取所有字段
        field = tClass.getDeclaredFields();


        for (Field value : field) {
            if (tableId >= 0) {
                break;
            }
            Annotation[] ids = value.getAnnotations();
            for (Annotation annotation : ids) {
                value.setAccessible(true);
                //获取方法名
                if (annotation instanceof TableId) {
                    //获取属性的名字
                    String name = value.getName();
                    //将属性名字的首字母大写
                    name = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
                    //整合出 get() 属性这个方法
                    Method m = tClass.getMethod("get" + name);
                    Object tableIdObj = m.invoke(t);
                    if (tableIdObj == null) {
                        throw new IllegalAccessException("更新语句tableId不能是null,必须大于0，请检查当前实体主键是否有值；表名--" + tableName);

                    } else {
                        tableId = (long) tableIdObj;
                    }

                    tableIdName = value.getName();
                    break;
                }
            }
        }
        //主键名称是空或者未获取到主键值
        if (StringUtils.isBlank(tableIdName) || tableId <= 0) {
            throw new IllegalAccessException("更新语句tableId必须大于0；【tableId=" + tableId + "】【tableIdName=" + tableIdName + "】");
        }
        Map<String, Object> updateChangeValues;
        Field fieldChangeValues;
        // 表示获取指定的一个公共的字段，包括继承的
        fieldChangeValues = tClass.getField("changeValues");
        //获取属性的名字
        String name = fieldChangeValues.getName();
        //将属性名字的首字母大写
        name = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
        //整合出 get() 属性这个方法
        Method m = tClass.getMethod("get" + name);
        //调用这个整合出来的get方法，强转成自己需要的类型
        updateChangeValues = (Map<String, Object>) m.invoke(t);
        //如果 changeValues 没有值则表示当前实体是new 出来 根据ID进行更新的
        if (updateChangeValues.isEmpty()) {
            for (Field value : field) {

                //获取属性的名字
                String fieldName = value.getName();
                //将属性名字的首字母大写
                fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName.substring(0, 1).toUpperCase());
                //整合出 get() 属性这个方法
                Method fieldM = tClass.getMethod("get" + fieldName);
                Object fieldObj = fieldM.invoke(t);
                if (fieldObj != null) {
                    updateChangeValues.put(value.getName(), fieldObj);
                }
            }
        }


        ExecuteSql execute = new ExecuteSql();
        execute.setTableName(tableName);
        execute.setDbname(dbName);
        execute.setMustExecute(isMustExecute);

        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();
        StringBuilder s4 = new StringBuilder();
        StringBuilder s5 = new StringBuilder();
        if (updateChangeValues.size() == 0) {
            return;
        }
        Map<String, Object> params = new HashMap<>();
        for (Map.Entry<String, Object> entry : updateChangeValues.entrySet()) {
            String mapKey = entry.getKey();
            Object mapValue = entry.getValue();
            if (mapKey.toLowerCase().equals(tableIdName.toLowerCase())) {
                continue;
            }
            s1.append(mapKey).append(" = ").append("#{").append(mapKey).append("},");
            s2.append(mapKey).append(" = ").append("'").append(mapValue).append("',");
            params.put(mapKey, mapValue);
        }
        s4.append("UPDATE ").append(tableName).append(" SET ").append(s1.toString(), 0, s1.length() - 1).append(" WHERE ").append(tableIdName).append(" = ").append(tableId).append(";");
        s5.append("UPDATE ").append(tableName).append(" SET ").append(s2.toString(), 0, s2.length() - 1).append(" WHERE ").append(tableIdName).append(" = ").append(tableId).append(";");
        execute.setExecuteSql(s4.toString());
        execute.setExecuteSqlLog(s5.toString());
        execute.setType(2);
        execute.setParamsList(params);
        asyncFlowData.getExecuteSQLs().add(execute);
    }


    /**
     * @param asyncFlowData 上下文
     * @param t             实体
     * @param dbName        数据库
     * @param isMustExecute 是否必须执行
     * @param <T>           泛型
     */
    public static <T> void operatorByEntity(AsyncFlowData asyncFlowData, T t, String dbName, boolean isMustExecute) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {

        //数据库不正确
        if (asyncFlowData.getDataBaseInfo().size() == 0 || !asyncFlowData.getDataBaseInfo().containsKey(dbName)) {
            throw new IllegalAccessException("当前节点位配置数据源，请检查asyncflow.yml文件是否有db-开头的数据库名称和对应SessionFactory的配置");
        }
        Class<?> tClass = t.getClass();
        //表名称
        String tableName = "";
        Annotation[] tableMethods = tClass.getAnnotations();
        for (Annotation annotation : tableMethods) {
            //获取方法名
            if (annotation instanceof TableName) {
                tableName = ((TableName) annotation).value();
                break;
            }
        }
        if (StringUtils.isBlank(dbName) || StringUtils.isBlank(tableName)) {
            throw new IllegalAccessException("表名为空，请检查当前实体是否有@TableName注解");
        }

        String tableIdName = "";
        long tableId = -1L;


        Field[] field;

        //获取所有字段
        field = tClass.getDeclaredFields();


        for (Field value : field) {
            if (tableId >= 0) {
                break;
            }
            Annotation[] ids = value.getAnnotations();
            for (Annotation annotation : ids) {
                value.setAccessible(true);
                //获取方法名
                if (annotation instanceof TableId) {
                    //获取属性的名字
                    String name = value.getName();
                    //将属性名字的首字母大写
                    name = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
                    //整合出 get() 属性这个方法
                    Method m = tClass.getMethod("get" + name);
                    Object tableIdObj = m.invoke(t);
                    if (tableIdObj == null) {
                        tableId = 0L;

                    } else {
                        tableId = (long) tableIdObj;
                    }

                    tableIdName = value.getName();
                    break;
                }
            }
        }
        //主键名称是空或者未获取到主键值
        if (StringUtils.isBlank(tableIdName) || tableId < 0) {
            return;
        }
        Map<String, Object> insertChangeValues = new HashMap<>();
        Map<String, Object> updateChangeValues = new HashMap<>();
        if (tableId == 0) {
            for (Field value : field) {

                //获取属性的名字
                String fieldName = value.getName();
                //将属性名字的首字母大写
                fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName.substring(0, 1).toUpperCase());
                //整合出 get() 属性这个方法
                Method fieldM = tClass.getMethod("get" + fieldName);
                Object fieldObj = fieldM.invoke(t);
                if (fieldObj != null) {
                    insertChangeValues.put(value.getName(), fieldObj);
                }
            }
        } else {
            Field fieldChangeValues;
            // 表示获取指定的一个公共的字段，包括继承的
            fieldChangeValues = tClass.getField("changeValues");
            //获取属性的名字
            String name = fieldChangeValues.getName();
            //将属性名字的首字母大写
            name = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
            //整合出 get() 属性这个方法
            Method m = tClass.getMethod("get" + name);
            //调用这个整合出来的get方法，强转成自己需要的类型
            updateChangeValues = (Map<String, Object>) m.invoke(t);
            //如果 changeValues 没有值则表示当前实体是new 出来 根据ID进行更新的
            if (updateChangeValues.isEmpty()) {
                for (Field value : field) {

                    //获取属性的名字
                    String fieldName = value.getName();
                    //将属性名字的首字母大写
                    fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName.substring(0, 1).toUpperCase());
                    //整合出 get() 属性这个方法
                    Method fieldM = tClass.getMethod("get" + fieldName);
                    Object fieldObj = fieldM.invoke(t);
                    if (fieldObj != null) {
                        updateChangeValues.put(value.getName(), fieldObj);
                    }
                }
            }
        }


        ExecuteSql execute = new ExecuteSql();
        execute.setTableName(tableName);
        execute.setDbname(dbName);
        execute.setMustExecute(isMustExecute);

        if (tableId == 0) {
            StringBuilder s1 = new StringBuilder();
            StringBuilder s2 = new StringBuilder();
            StringBuilder s3 = new StringBuilder();
            StringBuilder s4 = new StringBuilder();
            StringBuilder s5 = new StringBuilder();
            if (insertChangeValues.size() == 0) {
                return;
            }
            Map<String, Object> params = new HashMap<>();
            for (Map.Entry<String, Object> entry : insertChangeValues.entrySet()) {
                String mapKey = entry.getKey();
                Object mapValue = entry.getValue();
                if (mapKey.toLowerCase().equals(tableIdName.toLowerCase())) {
                    continue;
                }
                s1.append(mapKey).append(",");
                s2.append("#{").append(mapKey).append("},");
                s3.append("'").append(mapValue).append("',");
                params.put(mapKey, mapValue);

            }
            s4.append("INSERT INTO ").append(tableName).append("(").append(s1.toString(), 0, s1.toString().length() - 1).append(") VALUES (").append(s2.toString(), 0, s2.toString().length() - 1).append(");");
            s5.append("INSERT INTO ").append(tableName).append("(").append(s1.toString(), 0, s1.length() - 1).append(") VALUES (").append(s3.toString(), 0, s3.toString().length() - 1).append(");");
            execute.setExecuteSql(s4.toString());
            execute.setExecuteSqlLog(s5.toString());
            execute.setType(1);
            execute.setParamsList(params);
        } else {
            StringBuilder s1 = new StringBuilder();
            StringBuilder s2 = new StringBuilder();
            StringBuilder s4 = new StringBuilder();
            StringBuilder s5 = new StringBuilder();
            if (updateChangeValues.size() == 0) {
                return;
            }
            Map<String, Object> params = new HashMap<>();
            for (Map.Entry<String, Object> entry : updateChangeValues.entrySet()) {
                String mapKey = entry.getKey();
                Object mapValue = entry.getValue();
                if (mapKey.toLowerCase().equals(tableIdName.toLowerCase())) {
                    continue;
                }
                s1.append(mapKey).append(" = ").append("#{").append(mapKey).append("},");
                s2.append(mapKey).append(" = ").append("'").append(mapValue).append("',");
                params.put(mapKey, mapValue);
            }
            s4.append("UPDATE ").append(tableName).append(" SET ").append(s1.toString(), 0, s1.length() - 1).append(" WHERE ").append(tableIdName).append(" = ").append(tableId).append(";");
            s5.append("UPDATE ").append(tableName).append(" SET ").append(s2.toString(), 0, s2.length() - 1).append(" WHERE ").append(tableIdName).append(" = ").append(tableId).append(";");
            execute.setExecuteSql(s4.toString());
            execute.setExecuteSqlLog(s5.toString());
            execute.setType(2);
            execute.setParamsList(params);
        }


        asyncFlowData.getExecuteSQLs().add(execute);
    }

}
